package com.xiyuan.smartutils.logging;

import com.xiyuan.smartutils.StrUtils;
import com.xiyuan.smartutils.Validates;
import com.xiyuan.smartutils.logging.exception.UnsupportedLevelException;

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

/**
 * the description for class LoggerConfig
 *
 * @author xiyuan-lgz 2022-10-19 @version v1.0.0 新建与整理
 */
public class LogConfig {
    private Level minLevel = Level.INFO;// 日志级别 输出区间输出 格式为: "debug-warn"
    private Level maxLevel = Level.ERROR;// 日志级别 输出区间输出 格式为: "debug-warn"
    
    // %d{yyyy-MM-dd hh:mm:ss,fff}  配置时间 {中间是表达式}
    // %p   输出 日志级别
    // %t   输出线程名
    // %c   输出调用者类名    caller
    // %m   输出调用者方法名  methodName
    // %l   输出调用者方法 所在代码行号 lineNmber
    // %n   输出一个回车换行符，Windows平台为”\r\n”，Unix平台为”\n”输出日志信息换行
    // %%   输出一个‘%’
    private String pattern = "%d{yyyy-MM-dd hh:mm:ss,SSS} %p %t";// 日志输出格式
    
    
    /**
     * 是否输出控制台
     */
    private Boolean console = true;// 输出控制台
    
    /**
     * 是否输出到文件
     */
    private Boolean outFile;
    /**
     * 输出目录
     */
    private String dir = "./logs";// 输出目录
    /**
     * 文件名
     */
    private String fileName;//
    private Charset encoding = StandardCharsets.UTF_8;
    
    /**
     * 文件最大文件大小单位MiB, 超出部分则会重新创建文件 命名  ‘xxx.log’, 'xxx-2.log'
     */
    private Long maxFileSize = 0L;// 文件最大文件大小单位MiB, 超出部分则会重新创建文件 命名  ‘xxx.log’, 'xxx-2.log'
    
    /**
     * 默认保留文件 天数 0 永久保留
     */
    private Integer keepDay = 7; // 默认保留文件 天数 0 永久保留
    
    public boolean isConsole() {
        return console != null && console;
    }
    
    public LogConfig setConsole(Boolean console) {
        if (!check(console)) {
            return this;
        }
        
        this.console = console;
        return this;
    }
    
    public boolean isEnableLevel(Level level) {
        if (level == null) {
            return false;
        }
        
        return this.minLevel.val() <= level.val() && this.maxLevel.val() <= this.maxLevel.val();
    }
    
    
    public Level getMinLevel() {
        return minLevel;
    }
    
    public void setMinLevel(Level minLevel) {
        if (!check(minLevel)) {
            return;
        }
        this.minLevel = minLevel;
    }
    
    public Level getMaxLevel() {
        return maxLevel;
    }
    
    public void setMaxLevel(Level maxLevel) {
        if (!check(maxLevel)) {
            return;
        }
        this.maxLevel = maxLevel;
    }
    
    public void setLevel(String level) {
        if (!check(level)) {
            return;
        }
        List<Level> ls = Arrays.stream(StrUtils.trim(level, "").split("-"))
                .filter(Level::isSupport)
                .map(r -> {
                    try {
                        return Level.of(r);
                    }
                    catch (UnsupportedLevelException e) {
                        return null;
                    }
                })
                .sorted(Comparator.comparingInt(Level::val))
                .collect(Collectors.toList());
        
        if (ls.size() > 0) {
            this.minLevel = ls.get(0);
        }
        
        if (ls.size() > 1) {
            this.maxLevel = ls.get(ls.size() - 1);
        }
        
    }
    
    public boolean isOutFile() {
        return outFile != null && outFile;
    }
    
    public LogConfig setOutFile(Boolean outFile) {
        if (!check(outFile)) {
            return this;
        }
        
        this.outFile = outFile;
        return this;
    }
    
    public String getPattern() {
        return pattern;
    }
    
    public LogConfig setPattern(String pattern) {
        if (!check(pattern)) {
            return this;
        }
        
        pattern = StrUtils.trimLeft(pattern);
        if (Validates.isNotBlank(pattern)) {
            this.pattern = pattern;
        }
        
        return this;
    }
    
    public String getDir() {
        return dir;
    }
    
    public LogConfig setDir(String dir) {
        if (!check(dir)) {
            return this;
        }
        
        dir = StrUtils.trim(dir);
        if (Validates.isNotBlank(dir)) {
            this.dir = dir;
        }
        
        return this;
    }
    
    public String getFileName() {
        return fileName;
    }
    
    public LogConfig setFileName(String fileName) {
        if (!check(fileName)) {
            return this;
        }
        fileName = StrUtils.trim(fileName);
        if (Validates.isNotBlank(fileName)) {
            this.fileName = StrUtils.trimLeft(fileName, "/").trim();
        }
        return this;
    }
    
    public Charset getEncoding() {
        return encoding;
    }
    
    public LogConfig setEncoding(String encoding) {
        if (Charset.isSupported(encoding)) {
            this.setEncoding(Charset.forName(encoding));
        }
        
        return this;
    }
    
    public LogConfig setEncoding(Charset encoding) {
        if (!check(encoding)) {
            return this;
        }
        
        this.encoding = encoding;
        return this;
    }
    
    public long getMaxFileSize() {
        return maxFileSize == null ? 0 : maxFileSize;
    }
    
    public LogConfig setMaxFileSize(Long maxSize) {
        
        if (!check(maxSize)) {
            return this;
        }
        
        if (maxSize > 0) {
            this.maxFileSize = maxSize;
        }
        return this;
    }
    
    public LogConfig setMaxFileSize(String size) {
        if (!check(size)) {
            return this;
        }
        
        size = size.toLowerCase();
        size = size.replaceAll("gi?b?", "*" + (1 << 30));
        size = size.replaceAll("mi?b?", "*" + (1 << 20));
        size = size.replaceAll("ki?b?", "*" + (1 << 10));
        size = size.replaceAll("b", "");
        size = size.replaceAll("\\*+", "*");
        
        long target = 0;
        long curr = 0;
        for (int i = 0; i < size.length(); i++) {
            char ch = size.charAt(i);
            if (Validates.isWhitespace(ch)) {
                continue;
            }
            
            if (ch == '*') {
                target = target == 0 ? curr : target * curr;
                curr = 0;
                continue;
            }
            
            int v = ch - 48;
            if (v < 0 || v > 9) {
                return this;
            }
            
            curr = curr == 0 ? v : curr * 10 + v;
        }
        
        if (curr != 0) {
            target = target == 0 ? curr : target * curr;
        }
        
        if (target > 0) {
            this.maxFileSize = target;
        }
        
        return this;
    }
    
    public int getKeepDay() {
        return keepDay == null ? 0 : keepDay;
    }
    
    public LogConfig setKeepDay(Integer keepDay) {
        if (!check(keepDay)) {
            return this;
        }
        
        if (keepDay > 0) {
            this.keepDay = keepDay;
        }
        
        return this;
    }
    
    private boolean check(Object param) {
        return param != null;
    }
    
    public LogConfig init(ConfigItem item) {
        if (item != null) {
            this.setLevel(item.getLevel());
            this.setPattern(item.getPattern());
            this.setConsole(item.getConsole());
            this.setOutFile(item.getOutFile());
            this.setDir(item.getDir());
            this.setFileName(item.getFileName());
            this.setEncoding(item.getEncoding());
            this.setMaxFileSize(item.getMaxFileSize());
            this.setKeepDay(item.getKeepDay());
        }
        
        return this;
    }
}
